package com.example.excel.util;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.excel.write.builder.ExcelWriterTableBuilder;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.handler.WriteHandler;
import com.alibaba.excel.write.merge.LoopMergeStrategy;
import com.alibaba.excel.write.merge.OnceAbsoluteMergeStrategy;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.WriteTable;
import com.example.excel.config.CustomCellWriteHandler;
import com.example.excel.excel.EasyExcelMoreSheetEntity;
import com.example.excel.excel.EasyExcelMoreSheetMoreTableEntity;
import com.example.excel.excel.ExcelAnnotationValue;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.util.CellRangeAddress;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.net.URLEncoder;
import java.util.*;

/**
 * easyExcel工具
 */
@Slf4j
public class EasyExcelUtilsV1 {

    public static final String FILE_PATH = "F:\\zyDocument\\project\\easyExcel";

    public static final Map<String, List<ExcelAnnotationValue>> annotationValues = new HashMap<>();

    private static String outputStream(String fileName) {
        try {
            String path = FILE_PATH + new Date().getTime() + "/";
            String filePath = path + fileName + ".xls";
            File dir = new File(path);
            if (!dir.exists()) {
                dir.mkdirs();
            }
            File file = new File(filePath);
            if (file.exists()) {
                file.deleteOnExit();
            }
            file.createNewFile();
            return filePath;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 默认导出方式  单个sheet
     */
    public static String defaultExportOssUrl(List<?> list, Class<?> pojoClass, String fileName, String sheetName, Map<String, String> vars) {
        resetCLassAnnotationValue(pojoClass);
        setExportClassAnnotation(pojoClass, vars);
        String filePath = outputStream(fileName);
        EasyExcel.write(filePath, pojoClass)
                .sheet(sheetName)
                .registerWriteHandler(new CustomCellWriteHandler())
                .doWrite(list);
        return getExcelOssUrl(filePath, fileName);
    }

    /**
     * 默认导出方式  单个sheet
     */
    public static void defaultExportOssUrlResponse(List<?> list, Class<?> pojoClass, HttpServletResponse response, String sheetName, Map<String, String> vars) {
        resetCLassAnnotationValue(pojoClass);
        setExportClassAnnotation(pojoClass, vars);

        //需要合并的列
        int[] mergeColumeIndex = {0, 1, 2};
        //需要从第一行开始，列头第一行
        int mergeRowIndex = 1;

        try {
            // 设置为导出件格式为excel
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + URLEncoder.encode(sheetName, "utf-8") + ".xlsx");

            //合并最后一行的科目编码和科目名称
            OnceAbsoluteMergeStrategy onceAbsoluteMergeStrategy = new OnceAbsoluteMergeStrategy(4, 4, 0, 1);
            EasyExcel.write(response.getOutputStream(), pojoClass)
                    .sheet(sheetName)
                    .registerWriteHandler(new ExcelFillCellMergeStrategy(mergeRowIndex, mergeColumeIndex))
                    .registerWriteHandler(onceAbsoluteMergeStrategy)
                    .doWrite(list);

        } catch (Exception e) {
            log.error("下载文件失败!", e);
        }
        log.info("下载数据大小为:{}" + list.size());
    }

    /**
     * 默认导出方式  单个sheet
     */
    public static String defaultExportOssUrl(List<?> list, Class<?> pojoClass, CellWriteHandler handler, String fileName, String sheetName, Map<String, String> vars) {
        resetCLassAnnotationValue(pojoClass);
        setExportClassAnnotation(pojoClass, vars);
        String filePath = outputStream(fileName);
        EasyExcel.write(filePath, pojoClass)
                .sheet(sheetName)
                .registerWriteHandler(new CustomCellWriteHandler())
                .registerWriteHandler(handler)
                .doWrite(list);
        return getExcelOssUrl(filePath, fileName);
    }


    /**
     * 默认导出excel 单个sheet
     */
    public static String defaultExportOssUrl(List<?> list, Class<?> pojoClass, List<WriteHandler> handlers, String fileName, String sheetName, Map<String, String> vars) {
        resetCLassAnnotationValue(pojoClass);
        setExportClassAnnotation(pojoClass, vars);
        String filePath = outputStream(fileName);
        ExcelWriterSheetBuilder builder = EasyExcel.write(filePath, pojoClass)
                .sheet(sheetName);
        if (!ObjectUtils.isEmpty(handlers)) {
            for (WriteHandler handler : handlers) {
                builder.registerWriteHandler(handler);
            }
        }
        builder.doWrite(list);
        return getExcelOssUrl(filePath, fileName);
    }

    /**
     * 默认导出excel 单个sheet  多个table
     */
    public static String defaultExportOssUrl(EasyExcelMoreSheetMoreTableEntity entity, String fileName, Map<String, String> vars) {
        String filePath = outputStream(fileName);
        ExcelWriter excelWriter = EasyExcel.write(filePath).build();
        String sheetName = entity.getSheetName();
        List<WriteHandler> handlers = entity.getHandlers();
        List<EasyExcelMoreSheetEntity> list = entity.getList();
        try {
            WriteSheet writeSheet = EasyExcel.writerSheet(0, sheetName).build();
            for (int i = 0; i < list.size(); i++) {
                EasyExcelMoreSheetEntity sheetEntity = list.get(i);
                List date = sheetEntity.getList();
                Class clazz = sheetEntity.getClazz();
                resetCLassAnnotationValue(clazz);
                setExportClassAnnotation(clazz, vars);
                ExcelWriterTableBuilder tableBuilder = EasyExcel.writerTable(i);
                if (!ObjectUtils.isEmpty(handlers)) {
                    for (WriteHandler handler : handlers) {
                        tableBuilder.registerWriteHandler(handler);
                    }
                }
                WriteTable table = tableBuilder.head(clazz).needHead(true).build();
                excelWriter.write(date, writeSheet, table);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            excelWriter.finish();
        }
        return getExcelOssUrl(filePath, fileName);
    }


    /**
     * 多个sheet页导出
     */
    public static String moreSheetExportOssUrl(List<EasyExcelMoreSheetEntity> entities, String fileName) {
        String filePath = outputStream(fileName);
        ExcelWriter excelWriter = EasyExcel.write(filePath).build();
        try {
            for (int i = 0; i < entities.size(); i++) {
                EasyExcelMoreSheetEntity entity = entities.get(i);
                Class clazz = entity.getClazz();
                List list = entity.getList();
                Map<String, String> vars = entity.getVars();
                resetCLassAnnotationValue(clazz);
                setExportClassAnnotation(clazz, vars);
                String sheetName = entity.getSheetName();
                List<WriteHandler> handlers = entity.getHandlers();
                ExcelWriterSheetBuilder builder = EasyExcel.writerSheet(i, sheetName);
                if (!ObjectUtils.isEmpty(handlers)) {
                    for (WriteHandler handler : handlers) {
                        builder.registerWriteHandler(handler);
                    }
                }
                WriteSheet writeSheet = builder.head(clazz).build();
                excelWriter.write(list, writeSheet);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            excelWriter.finish();
        }
        return getExcelOssUrl(filePath, fileName);
    }


    @SuppressWarnings("unchecked")
    public static String moreSheetMoreTableExportOssUrl(List<EasyExcelMoreSheetMoreTableEntity> entities, String fileName) {
        String filePath = outputStream(fileName);
        ExcelWriter excelWriter = EasyExcel.write(filePath).build();
        try {
            for (int i = 0; i < entities.size(); i++) {
                EasyExcelMoreSheetMoreTableEntity entity = entities.get(i);
                List<EasyExcelMoreSheetEntity> list = entity.getList();
                String sheetName = entity.getSheetName();
                List<WriteHandler> handlers = entity.getHandlers();
                ExcelWriterSheetBuilder sheetBuilder = EasyExcel.writerSheet(i, sheetName);
                if (!ObjectUtils.isEmpty(handlers)) {
                    for (WriteHandler handler : handlers) {
                        sheetBuilder.registerWriteHandler(handler);
                    }
                }
                //创建sheet
                WriteSheet writeSheet = sheetBuilder.build();
                //创建table
                Assert.isTrue(!ObjectUtils.isEmpty(list), "缺少table数据");
                for (int j = 0; j < list.size(); j++) {
                    EasyExcelMoreSheetEntity tableEntity = list.get(j);
                    Map<String, String> vars = tableEntity.getVars();
                    List<?> date = tableEntity.getList();
                    Class<?> clazz = tableEntity.getClazz();
                    resetCLassAnnotationValue(clazz);
                    setExportClassAnnotation(clazz, vars);
                    ExcelWriterTableBuilder tableBuilder = EasyExcel.writerTable(j);

                    if (j > 0) {
                        tableBuilder.relativeHeadRowIndex(2);
                    }
                    WriteTable table = tableBuilder.head(clazz).needHead(true).build();
                    excelWriter.write(date, writeSheet, table);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            excelWriter.finish();
        }
        return getExcelOssUrl(filePath, fileName);
    }

    public static void defaultExport(List<?> list, Class<?> pojoClass, String filePath, String sheetName) {
        EasyExcel.write(filePath, pojoClass)
                .sheet(sheetName)
                .registerWriteHandler(new CustomCellWriteHandler())
                .doWrite(list);
    }


    private static String getExcelOssUrl(String filePath, String fileName) {
        InputStream in = null;
        try {
            //临时缓冲区
            in = new FileInputStream(filePath);
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 此处可以调用腾讯云的cos 或者阿里云的oss todo
        String url = "";
        return url;
    }


    public static void setExportClassAnnotation(Class<?> clazz, Map<String, String> map) {
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            ExcelProperty property = field.getAnnotation(ExcelProperty.class);
            if (property != null) {
                List<String> newValues = new ArrayList<>();
                String[] values = property.value();
                for (String value : values) {
                    value = replace(value, map);
                    newValues.add(value);
                }
                InvocationHandler h = Proxy.getInvocationHandler(property);
                try {
                    Field annotationField = h.getClass().getDeclaredField("memberValues");
                    annotationField.setAccessible(true);
                    Map memberValues = (Map) annotationField.get(h);
                    memberValues.put("value", newValues.toArray(new String[]{}));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private static void resetCLassAnnotationValue(Class<?> clazz) {
        String className = clazz.getSimpleName();
        List<ExcelAnnotationValue> values = annotationValues.get(className);
        if (ObjectUtils.isEmpty(values)) {
            //如果静态资源是空的，保存
            Field[] fields = clazz.getDeclaredFields();
            values = new ArrayList<>();
            for (Field field : fields) {
                ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
                if (!ObjectUtils.isEmpty(excelProperty)) {
                    String[] vs = excelProperty.value();
                    ExcelAnnotationValue value = new ExcelAnnotationValue()
                            .setFieldName(field.getName())
                            .setValues(vs);
                    values.add(value);
                }
            }
            annotationValues.put(className, values);
            return;
        }
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            String fieldName = field.getName();
            ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
            if (!ObjectUtils.isEmpty(excelProperty)) {
                ExcelAnnotationValue value = values.stream().filter(v -> v.getFieldName().equals(fieldName)).findFirst().orElse(null);
                if (!ObjectUtils.isEmpty(value)) {
                    String[] oldValues = value.getValues();
                    InvocationHandler handler = Proxy.getInvocationHandler(excelProperty);
                    try {
                        Field annotationField = handler.getClass().getDeclaredField("memberValues");
                        annotationField.setAccessible(true);
                        Map memberValues = (Map) annotationField.get(handler);
                        memberValues.put("value", oldValues);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }


    public static String replace(String el, Map<String, String> map) {
        if (map == null) {
            return el;
        }
        String evl = el;
        for (Map.Entry<String, String> m : map.entrySet()) {
            String key = m.getKey();
            String value = m.getValue();
            el = el.replaceAll("#\\{" + key + "\\}", value);
            if (!evl.equals(el)) {
                return el;
            }
        }
        return el;
    }


}